﻿namespace Zen.BlackBerry.Web
{
	using System;
	using System.Web.Mvc;
	using System.Web.Routing;
	using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
	using Microsoft.Practices.ServiceLocation;
	using Microsoft.Practices.Unity;
	using Zen.BlackBerry.Acknowledgement;
	using Zen.BlackBerry.Common;
	using Zen.BlackBerry.Configuration;
	using Zen.BlackBerry.Pap;
	using Zen.BlackBerry.Pap.Web;
	using Zen.BlackBerry.Push;
	using Zen.BlackBerry.Push.AppManagement;
	using Zen.BlackBerry.Push.Count;
	using Zen.BlackBerry.Push.Repositories;
	using Zen.BlackBerry.Push.Request;
	using Zen.BlackBerry.Push.Stats;
	using Zen.BlackBerry.Subscription;
	using Zen.BlackBerry.Subscription.Auth;
	using Zen.BlackBerry.Subscription.Web;

	public class WebBootstrapper
	{
		#region Private Types
		/// <summary>
		/// <c>UnityControllerFactory</c> extends <see cref="DefaultControllerFactory"/>
		/// to use Unity IoC when creating MVC controller instances.
		/// </summary>
		private class UnityControllerFactory : DefaultControllerFactory
		{
			private IUnityContainer _container;

			public UnityControllerFactory()
			{
				_container = ServiceLocator.Current.GetInstance<IUnityContainer>();
			}

			protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
			{
				if (controllerType == null)
				{
					return null;
				}
				return (IController)_container.Resolve(controllerType);
			}

			public override void ReleaseController(IController controller)
			{
				_container.Teardown(controller);
			}
		}

		// TODO: Remove this class and replace with ConfigurationPushProperties
		//	and perhaps a DatabasePushProperties class that can read from config
		//	and database respectively.
		private class PushProperties : IPushProperties
		{
			#region IPushProperties Members
			public int AcknowledgementBatchSize
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int AcknowledgementMaxQueueSize
			{
				get
				{
					return 15;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int AcknowledgementPushLookupRetryDelay
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string DatabaseType
			{
				get
				{
					return "RDBMS";
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public TimeSpan DefaultDeliverBefore
			{
				get
				{
					return TimeSpan.FromDays(30);
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string DtdDeclarationEnterprise
			{
				get
				{
					return string.Empty;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string DtdDeclarationPublic
			{
				get
				{
					return string.Empty;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string EnterprisePpgAddress
			{
				get
				{
					return string.Empty;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string PublicPpgAddress
			{
				get
				{
					return "https://pushapi.eval.blackberry.com";
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int HttpConnectionTimeout
			{
				get
				{
					return 20;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public bool IsHttpPersistent
			{
				get
				{
					return true;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int HttpReadTimeout
			{
				get
				{
					return 20;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int MaxInClauseValues
			{
				get
				{
					return 20;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public char[] ParserSpecialCharacters
			{
				get
				{
					return new char[0];
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int PushAppDeleteUnsubscribeQueueSize
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int PushAppDeleteUnsubscribeTimeout
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int PushAppSubscriberDeleteTimeout
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int PushRequestDetailFindMaxResults
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public TimeSpan PushStatsUpdateFrequency
			{
				get
				{
					return TimeSpan.FromMinutes(5);
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int PushStatsUpdateQueueSize
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int RegeneratePushIdMaxAttempts
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionFindMaxResults
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionMatchingMaxThreads
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionMatchingQueueSize
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionPpgSyncBatchSize
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionPpgSyncMaxThreads
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionPpgSyncQueueSize
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionPpgSyncTimeout
			{
				get
				{
					return 10;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string SubscriptionBaseUrl
			{
				get
				{
					return "https://pushapi.eval.blackberry.com";
				}
				set
				{
				}
			}

			public string SubscriptionQueryUrl
			{
				get
				{
					return string.Format("{0}/mss/PD_cpSubQuery", SubscriptionBaseUrl);
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string SubscriptionDeregistrationUrl
			{
				get
				{
					return string.Format("{0}/mss/PD_cpDeregUser?pin=", SubscriptionBaseUrl);
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string SubscriptionResumeUrl
			{
				get
				{
					return string.Format("{0}/mss/PD_cpSub?action=resume&pin=", SubscriptionBaseUrl);
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string SubscriptionSuspendUrl
			{
				get
				{
					return string.Format("{0}/mss/PD_cpSub?action=suspend&pin=", SubscriptionBaseUrl);
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionValidationBatchSize
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionValidationHighWaterMark
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string WebSignalsDeregistrationUrl
			{
				get
				{
					return string.Empty;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string WebSignalsRegistrationUrl
			{
				get
				{
					return string.Empty;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string WebSignalsResumeUrl
			{
				get
				{
					return string.Empty;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public string WebSignalsSuspendUrl
			{
				get
				{
					return string.Empty;
				}
				set
				{
					throw new NotImplementedException();
				}
			}

			public int SubscriptionNotifyMaxRetries
			{
				get
				{
					return 5;
				}
				set
				{
					throw new NotImplementedException();
				}
			}
			#endregion
		}
		#endregion

		/// <summary>
		/// Gets or sets a value indicating whether this web-site instance
		/// needs to receive Push Plus acknowledgements.
		/// </summary>
		/// <value>
		/// <c>true</c> if Push Plus acknowledgements are used; otherwise,
		/// <c>false</c>.
		/// </value>
		public bool UseAcknowledgements
		{
			get;
			set;
		}

		/// <summary>
		/// Gets or sets the name of the push-id domain.
		/// </summary>
		/// <value>The name of the push id domain.</value>
		/// <remarks>
		/// The value here is added to the end of all Push-Ids generated by
		/// the Push Service SDK to provide a namespace for Push-Ids from this
		/// site. This is recommended in order to avoid Push-Id collisions.
		/// </remarks>
		public string PushIdDomainName
		{
			get;
			set;
		}

		public IUnityContainer Container
		{
			get;
			private set;
		}

		public void Run()
		{
			// Create container and setup common service locator
			Container = CreateContainer();

			// Setup unity container
			ConfigureContainer();
		}

		protected virtual IUnityContainer CreateContainer()
		{
			return EnterpriseLibraryContainer.Current.GetInstance<IUnityContainer>();
		}

		protected virtual void ConfigureContainer()
		{
			// Setup Enterprise Library locator
			IServiceLocator locator = new UnityServiceLocator(Container);
			ServiceLocator.SetLocatorProvider(() => locator);

			// Configure Push Service SDK
			ConfigureLowLevelServices();
			ConfigureHighLevelRepositories();
			ConfigureHighLevelServices();

			// Configure MVC
			ConfigureAreas();
			ConfigureGlobalFilters(GlobalFilters.Filters);
			ConfigureRoutes(RouteTable.Routes);

			// Ensure MVC uses Unity to build controller objects
			ControllerBuilder.Current.SetControllerFactory(
				typeof(UnityControllerFactory));
		}

		public virtual void ConfigureAreas()
		{
			AreaRegistration.RegisterAllAreas();
		}

		public virtual void ConfigureGlobalFilters(GlobalFilterCollection filters)
		{
			filters.Add(new HandleErrorAttribute());
		}

		public virtual void ConfigureRoutes(RouteCollection routes)
		{
			routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

			// Register route handlers for subscription messages
			routes.Add("Subscribe", new Route(
				"{controller}/{action}", null,
				new RouteValueDictionary
				{
					{ "controller", "Notification" },
					{ "action", "Subscribe" }
				},
				new SubscribeRouteHandler()));
			routes.Add("Suspend", new Route(
				"{controller}/{action}", null,
				new RouteValueDictionary
				{
					{ "controller", "Notification" },
					{ "action", "Suspend" }
				},
				new SuspendRouteHandler()));
			routes.Add("Resume", new Route(
				"{controller}/{action}", null,
				new RouteValueDictionary
				{
					{ "controller", "Notification" },
					{ "action", "Resume" }
				},
				new ResumeRouteHandler()));
			routes.Add("Unsubscribe", new Route(
				"{controller}/{action}", null,
				new RouteValueDictionary
				{
					{ "controller", "Notification" },
					{ "action", "Unsubscribe" }
				},
				new UnsubscribeRouteHandler()));

			// Register route handlers for inbound acknowledgements
			if (UseAcknowledgements)
			{
				routes.Add("Acknowledge", new Route(
					"{controller}/{action}", null,
					new RouteValueDictionary
					{
						{ "controller", "Notification" },
						{ "action", "Acknowledge" }
					},
					new PushNotificationRouteHandler()));
			}

			routes.MapRoute(
				"Default", // Route name
				"{controller}/{action}/{id}", // URL with parameters
				new
				{
					controller = "Home",
					action = "Index",
					id = UrlParameter.Optional
				});
		}

		/// <summary>
		/// Configures the low-level Push API services.
		/// </summary>
		/// <remarks>
		/// Registers the following services:
		/// IIdGenerator, IBoundaryGenerator, IPapService
		/// </remarks>
		protected virtual void ConfigureLowLevelServices()
		{
			// Setup Push Properties
			if (!Container.IsRegistered<IPushProperties>())
			{
				Container.RegisterType<IPushProperties, PushProperties>(
					new ContainerControlledLifetimeManager());
			}

			// Setup Push ID generator passing in domain name
			if (!Container.IsRegistered<IIdGenerator>())
			{
				Container.RegisterType<IIdGenerator, IdGenerator>(
					new ContainerControlledLifetimeManager(),
					new InjectionConstructor(PushIdDomainName));
			}

			// Setup Boundary generator
			if (!Container.IsRegistered<IBoundaryGenerator>())
			{
				Container.RegisterType<IBoundaryGenerator, BoundaryGenerator>(
					new ContainerControlledLifetimeManager());
			}

			// Setup Pap service if not already registered
			if (!Container.IsRegistered<IPapService>())
			{
				if (!UseAcknowledgements)
				{
					Container.RegisterType<IPapService, PapService>(
						new ContainerControlledLifetimeManager());
				}
				else
				{
					Container.RegisterType<IPapService, AcknowledgementPapService>(
						new ContainerControlledLifetimeManager());
				}
			}
		}

		/// <summary>
		/// Configures the high-level Push API services.
		/// </summary>
		protected virtual void ConfigureHighLevelServices()
		{
			// Setup high-level services
			Container.RegisterType<IPushApplicationService, PushApplicationService>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushCountService, PushCountService>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushRequestService, PushRequestService>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushRequestDetailService, PushRequestDetailService>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushStatsService, PushStatsService>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushStatsBatchUpdaterService, PushStatsBatchUpdaterService>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<ISubscriptionService, SubscriptionService>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushService, PushService>(
				new ContainerControlledLifetimeManager());

			// Content-provider interfaces are next
			ConfigureContentProviderServices();

			if (!Container.IsRegistered<IContentProviderSubscriptionService>())
			{
				Container.RegisterType<IContentProviderSubscriptionService, ContentProviderSubscriptionService>(
					new ContainerControlledLifetimeManager());
			}
			if (!Container.IsRegistered<IContentProviderAuthenticationService>())
			{
				Container.RegisterType<IContentProviderAuthenticationService, ContentProviderAuthenticationService>(
					new ContainerControlledLifetimeManager());
			}
		}

		/// <summary>
		/// Configures the content provider services.
		/// </summary>
		protected virtual void ConfigureContentProviderServices()
		{
		}

		/// <summary>
		/// Configures the high-level Push API repositories.
		/// </summary>
		protected virtual void ConfigureHighLevelRepositories()
		{
			Container.RegisterType<IPushApplicationRepository, PushApplicationRepository>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushCountRepository, PushCountRepository>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushRequestRepository, PushRequestRepository>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushRequestDetailRepository, PushRequestDetailRepository>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<IPushStatsRepository, PushStatsRepository>(
				new ContainerControlledLifetimeManager());
			Container.RegisterType<ISubscriptionRepository, SubscriptionRepository>(
				new ContainerControlledLifetimeManager());
		}
	}
}